from sqlalchemy import and_, select
from sqlalchemy.exc import DBAPIError, IntegrityError

from src.core.exceptions.common import RepoError
from src.core.models.dto.activation_code import ActivationCodeDTO
from src.core.models.enums import SubscriptionType
from src.database.converters.activation_code import (
    convert_db_model_to_activation_code_dto,
)
from src.database.exception_mapper import exception_mapper
from src.database.models import ActivationCode
from src.database.repositories.base import SQLAlchemyRepo


class ActivationCodeReaderImpl(SQLAlchemyRepo):
    @exception_mapper
    async def get_activation_code_by_name(
        self, code_name: str, subscription_type: SubscriptionType
    ) -> ActivationCodeDTO | None:
        """Получаем код активации по имени"""

        query = select(ActivationCode).where(
            and_(
                ActivationCode.title == code_name,
                ActivationCode.subscription_type == subscription_type,
            )
        )

        activation_code: ActivationCode | None = await self._session.scalar(query)

        if not activation_code:
            return None

        return convert_db_model_to_activation_code_dto(activation_code)


class ActivationCodeRepoImpl(SQLAlchemyRepo):
    @exception_mapper
    async def acquire_activation_code_by_id(
        self, promocode_id: int
    ) -> ActivationCode | None:
        query = select(ActivationCode).where(ActivationCode.id == promocode_id)
        code: ActivationCode | None = await self._session.scalar(query)

        return code

    @exception_mapper
    async def update_activation_code(self, code: ActivationCode) -> None:
        try:
            await self._session.merge(code)
        except IntegrityError as err:
            self._parse_error(err)

    @staticmethod
    def _parse_error(err: DBAPIError) -> None:
        raise RepoError from err
